using System;
using System.Collections.Generic;
using System.Text;
using SchemaExplorer;
using CodeSmith.Engine;
using System.Data;
using System.ComponentModel;
using System.Diagnostics;

namespace CommonLib
{
    public class TemplateBase : CodeSmith.Engine.CodeTemplate
    {
        public delegate string ColumnToStringDelegate(ColumnSchema col);

        protected Dictionary<string, string> columnAbbrNameDict = new Dictionary<string, string>();
        protected Dictionary<string, string> dictNetType2CSharpType = new Dictionary<string, string>();
        protected Dictionary<string, string> dictCSharpTypeDefValue = new Dictionary<string, string>();
        protected Dictionary<string, string> dictCSharpType2NetType = new Dictionary<string, string>();

        public TemplateBase()
        {
            dictNetType2CSharpType.Add("Boolean", "bool");
            dictNetType2CSharpType.Add("Byte", "byte");
            dictNetType2CSharpType.Add("Byte[]", "byte[]");
            dictNetType2CSharpType.Add("SByte", "sbyte");
            dictNetType2CSharpType.Add("Char", "char");
            dictNetType2CSharpType.Add("Decimal", "decimal");
            dictNetType2CSharpType.Add("Double", "double");
            dictNetType2CSharpType.Add("Single", "float");
            dictNetType2CSharpType.Add("Int32", "int");
            dictNetType2CSharpType.Add("UInt32", "uint");
            dictNetType2CSharpType.Add("Int64", "long");
            dictNetType2CSharpType.Add("UInt64", "ulong");
            dictNetType2CSharpType.Add("Object", "object");
            dictNetType2CSharpType.Add("Int16", "short");
            dictNetType2CSharpType.Add("UInt16", "ushort");
            dictNetType2CSharpType.Add("String", "string");

            foreach (string key in dictNetType2CSharpType.Keys)
            {
                dictCSharpType2NetType.Add(dictNetType2CSharpType[key], key);
            }

            dictCSharpTypeDefValue.Add("bool", "false");
            dictCSharpTypeDefValue.Add("byte", "0");
            dictCSharpTypeDefValue.Add("sbyte", "0");
            dictCSharpTypeDefValue.Add("char", "0");
            dictCSharpTypeDefValue.Add("decimal", "0");
            dictCSharpTypeDefValue.Add("double", "0.0F");
            dictCSharpTypeDefValue.Add("float", "0.0F");
            dictCSharpTypeDefValue.Add("int", "0");
            dictCSharpTypeDefValue.Add("uint", "0");
            dictCSharpTypeDefValue.Add("long", "0");
            dictCSharpTypeDefValue.Add("ulong", "0");
            dictCSharpTypeDefValue.Add("object", "null");
            dictCSharpTypeDefValue.Add("short", "0");
            dictCSharpTypeDefValue.Add("ushort", "0");
            dictCSharpTypeDefValue.Add("string", "String.Empty");
            dictCSharpTypeDefValue.Add("DateTime", "DateTime.MinValue");
            dictCSharpTypeDefValue.Add("TimeSpan", "TimeSpan.Zero");
            dictCSharpTypeDefValue.Add("Guid", "Guid.Empty");
            dictCSharpTypeDefValue.Add("byte[]", "new byte[0]");

            columnAbbrNameDict.Add("pid", "parent_id");
            columnAbbrNameDict.Add("desc", "description");
            columnAbbrNameDict.Add("attrib", "attribute");
            columnAbbrNameDict.Add("msg", "message");
            columnAbbrNameDict.Add("pri", "priority");
            columnAbbrNameDict.Add("play_pri", "play_priority");
            columnAbbrNameDict.Add("op_type", "operation_type");
            columnAbbrNameDict.Add("op_result", "operation_result");
            columnAbbrNameDict.Add("op_user_id", "operate_user_id");
            columnAbbrNameDict.Add("op_entity_type", "operate_entity_type");
            columnAbbrNameDict.Add("op_entity_id", "operate_entity_id");
            columnAbbrNameDict.Add("op_entity_xml", "operate_entity_xml");
            columnAbbrNameDict.Add("salt", "password_salt");
            columnAbbrNameDict.Add("client_env", "client_environment");
            columnAbbrNameDict.Add("ref_name", "reference_name");
            columnAbbrNameDict.Add("ref_href", "reference_href");
            columnAbbrNameDict.Add("donation_proj_id", "donation_project_id");
        }

        private string sourceTablePrefixName = "tb_";
        [Category("Naming")]
        [DefaultValue("tb_")]
        [TypeConverter("String")]
        [Browsable(true)]
        public string SourceTablePrefixName
        {
            get { return sourceTablePrefixName; }
            set { sourceTablePrefixName = value; }
        }


        public enum NameType
        {
            PropertyName,
            PropertyDescription,
            PropertyType,
            PropertyDefaultValue,
            MemberName,
            ClassName,
            ClassDescription,
            XmlElementName,
            XmlAttributeName,
            JsonPropertyName,
            DataProviderName,
        }

        private string GetColumnFullName(ColumnSchema col)
        {
            if (col.IsPrimaryKeyMember) return "id";
            if (columnAbbrNameDict.ContainsKey(col.Name)) return columnAbbrNameDict[col.Name];
            return col.Name;
        }

        public string GetCleanTableName(TableSchema table)
        {
            if (!String.IsNullOrEmpty(SourceTablePrefixName) && table.Name.StartsWith(SourceTablePrefixName))
            {
                return table.Name.Remove(0, SourceTablePrefixName.Length);
            }
            else
            {
                return table.Name;
            }
        }

        public string GetName(string name, NameType nameType)
        {
            switch (nameType)
            {
                case NameType.PropertyName:
                    return StringUtil.ToPascalCase(name);
                case NameType.MemberName:
                    return StringUtil.ToCamelCase(name);
                case NameType.ClassName:
                    if (!String.IsNullOrEmpty(SourceTablePrefixName) && name.StartsWith(SourceTablePrefixName))
                    {
                        return StringUtil.ToPascalCase(name.Remove(0, SourceTablePrefixName.Length));
                    }
                    else
                    {
                        return StringUtil.ToPascalCase(name);
                    }
                case NameType.XmlElementName:
                    return GetName(name, NameType.ClassName);
                case NameType.DataProviderName:
                    return GetName(name, NameType.ClassName) + "DataProvider";
                default:
                    return name;
            }
        }

        public string GetName(ColumnSchema column, NameType nameType)
        {
            string colName = GetColumnFullName(column);
            switch (nameType)
            {
                case NameType.PropertyName:
                case NameType.MemberName:
                    return GetName(colName, nameType);
                case NameType.PropertyDescription:
                    if (String.IsNullOrEmpty(column.Description))
                    {
                        return GetName(colName, NameType.ClassName);
                    }
                    else
                    {
                        return column.Description;
                    }
                case NameType.PropertyType:
                    return this.GetCSharpType(column);
                case NameType.PropertyDefaultValue:
                    return this.GetCSharpTypeDefaultValue(column);
                case NameType.XmlAttributeName:
                    return GetName(column.Name, NameType.MemberName);
                case NameType.JsonPropertyName:
                    return GetName(column.Name, NameType.MemberName);
                default:
                    return GetName(colName, nameType);
            }
        }

        public string GetName(TableSchema table, NameType nameType)
        {
            switch (nameType)
            {
                case NameType.ClassDescription:
                    if (String.IsNullOrEmpty(table.Description))
                    {
                        return GetName(GetCleanTableName(table), NameType.ClassName);
                    }
                    else
                    {
                        if (table.Description.EndsWith("��"))
                        {
                            return table.Description.Substring(0, table.Description.Length - 1);
                        }
                        else
                        {
                            return table.Description;
                        }
                    }
                default:
                    return GetName(GetCleanTableName(table), nameType);
            }
        }

        public string[] ConvertColumnsToStringArray(ICollection<ColumnSchema> cols, ColumnToStringDelegate converter)
        {
            List<string> list = new List<string>();
            foreach (ColumnSchema col in cols)
            {
                list.Add(converter(col));
            }
            return list.ToArray();
        }

        public bool IsColumnAsXmlAttribute(ColumnSchema column)
        {
            switch (column.Name.ToLower())
            {
                case "id":
                case "time_stamp":
                    return true;
                default:
                    return false;
            }
        }

        public string GetXmlValueWriteCode(ColumnSchema column, string prefix)
        {
            string postfix = IsNullableValueType(column) ? ".Value" : "";
            if (IsEnumType(column))
            {
                return prefix + GetName(column, NameType.PropertyName) + postfix + ".ToString()";
            }
            else if (GetCSharpType(column) == "string")
            {
                return prefix + GetName(column, NameType.PropertyName) + postfix;
            }
            else if (IsTimeStampColumn(column))
            {
                return prefix + GetName(column, NameType.PropertyName) + postfix + ".Ticks.ToString()";
            }
            else if (GetDotNetType(column) == "DateTime")
            {
                return "XmlConvert.ToString(" + prefix + GetName(column, NameType.PropertyName) + postfix + ", XmlDateTimeSerializationMode.Utc)";
            }
            else
            {
                return "XmlConvert.ToString(" + prefix + GetName(column, NameType.PropertyName) + postfix + ")";
            }
        }

        public string GetXmlValueReadCode(ColumnSchema column, string varName)
        {
            if (IsEnumType(column))
            {
                return String.Format("{2} = ({0})Enum.Parse(typeof({0}), {1}, true)", GetEnumName(column), varName, GetName(column, NameType.MemberName));
            }
            else if (GetCSharpType(column) == "string")
            {
                return GetName(column, NameType.MemberName) + " = " + varName;
            }
            else if (IsTimeStampColumn(column))
            {
                return GetName(column, NameType.MemberName) + " = new DateTime(long.Parse(" + varName + "))";
            }
            else if (GetDotNetType(column) == "DateTime")
            {
                return GetName(column, NameType.MemberName) + " = " + "XmlConvert.To" + GetDotNetType(column) + "(" + varName + ", XmlDateTimeSerializationMode.Utc)";
            }
            else
            {
                return GetName(column, NameType.MemberName) + " = " + "XmlConvert.To" + GetDotNetType(column) + "(" + varName + ")";
            }
        }

        public string GetDataProviderSetColumnValueCode(ColumnSchema col, string entityName)
        {
            string propCode = String.Format("{0}.{1}", entityName, GetName(col, NameType.PropertyName));
            string finalCode = propCode;


            if (IsEnumType(col))
            {
                if (col.SystemType.IsValueType)
                {
                    finalCode = "(" + GetCSharpType(col.SystemType.Name) + ")" + propCode;
                }
                else
                {
                    finalCode = GetCSharpType(col.SystemType.Name) + ".ToString()";
                }
            }
            
            if (col.AllowDBNull)
            {
                if (col.SystemType.IsValueType)
                {
                    finalCode = String.Format("{0} == null ? DBNull.Value : (object){0}.Value", finalCode);
                }
                else
                {
                    finalCode = String.Format("{0} == null ? DBNull.Value : (object){0}", finalCode);
                }
            }
            else if (col.SystemType == typeof(string))
            {
                finalCode = String.Format("{0} == null ? String.Empty : {0}", finalCode);
            }

            return finalCode;
        }

        //public string GetXmlValueReadCode(ColumnSchema column, string varName)
        //{
        //    if (IsEnumType(column))
        //    {
        //        return String.Format("{2} = ({0})Enum.Parse(typeof({0}), {1}, true)", GetEnumName(column), varName, GetName(column, NameType.MemberName));
        //    }
        //    else if (GetCSharpType(column) == "string")
        //    {
        //        return GetName(column, NameType.MemberName) + " = " + varName;
        //    }
        //    else if (IsTimeStampColumn(column))
        //    {
        //        return GetName(column, NameType.MemberName) + " = new DateTime(long.Parse(" + varName + "))";
        //    }
        //    else if (GetDotNetType(column) == "DateTime")
        //    {
        //        return GetName(column, NameType.MemberName) + " = " + "XmlConvert.To" + GetDotNetType(column) + "(" + varName + ", XmlDateTimeSerializationMode.Utc)";
        //    }
        //    else
        //    {
        //        return GetName(column, NameType.MemberName) + " = " + "XmlConvert.To" + GetDotNetType(column) + "(" + varName + ")";
        //    }
        //}

        public string GetJsonValueWriteCode(ColumnSchema column, string prefix)
        {
            string postfix = IsNullableValueType(column) ? ".Value" : "";
            if (IsEnumType(column))
            {
                return "(int)" + prefix + GetName(column, NameType.PropertyName) + postfix;
            }
            else if (IsTimeStampColumn(column))
            {
                return prefix + GetName(column, NameType.PropertyName) + postfix + ".Ticks";
            }
            else
            {
                return prefix + GetName(column, NameType.PropertyName) + postfix;
            }
        }

        public string GetJsonValueReadCode(ColumnSchema column, string varName, string prefix)
        {
            if (IsEnumType(column))
            {
                return String.Format("{2} = ({0})(DbConvert.ToNullableInt32({1}) ?? (int){3}{2})",
                    GetEnumName(column),
                    varName,
                    GetName(column, NameType.MemberName),
                    prefix);
            }
            else if (GetCSharpType(column) == "string")
            {
                return String.Format("{0} = Convert.ToString({1}) ?? {2}{0}",
                    GetName(column, NameType.MemberName),
                    varName,
                    prefix);
            }
            else if (IsTimeStampColumn(column))
            {
                return GetName(column, NameType.MemberName) + " = new DateTime(DbConvert.ToNullableInt64(" + varName + ") ?? 0)";
            }
            else
            {
                return String.Format("{0} = DbConvert.ToNullable{1}({2}) ?? {3}{0}",
                    GetName(column, NameType.MemberName),
                    GetDotNetType(column),
                    varName,
                    prefix);
            }
        }

        public string GetDotNetType(string type)
        {
            if (dictCSharpType2NetType.ContainsKey(type)) return dictCSharpType2NetType[type];
            return type;
        }

        public string GetDbColumnSize(ColumnSchema column)
        {
            if (column.DataType == DbType.String ||
                column.DataType == DbType.StringFixedLength)
            {
                if (column.NativeType.IndexOf("text") > -1)
                {
                    return String.Empty;
                }
                else
                {
                    return ", " + (column.Size / 3).ToString();
                }
            }
            else
            {
                return column.Size > 0 ? ", " + column.Size.ToString() : "";
            }
        }
        

        public string GetDbType(ColumnSchema column)
        {
            if (column.DataType == DbType.Object && column.NativeType.IndexOf("text") > -1)
            {
                return DbType.String.ToString();
            }
            else
            {
                return column.DataType.ToString();
            }
        }

        public bool IsNullableValueType(ColumnSchema column)
        {
            return column.AllowDBNull && column.SystemType.IsValueType;
        }

        public string GetDotNetType(ColumnSchema column)
        {
            if (IsEnumType(column))
            {
                return GetEnumName(column);
            }
            else
            {
                if (column.NativeType.IndexOf("text") > -1)
                {
                    return "String";
                }
                else if (column.NativeType.IndexOf("bit") > -1)
                {
                    return "Boolean";
                }
                else
                {
                    return column.SystemType.Name;
                }
            }
        }

        public string GetCSharpType(string type)
        {
            if (dictNetType2CSharpType.ContainsKey(type)) return dictNetType2CSharpType[type];
            return type;
        }

        public string GetCSharpType(ColumnSchema column)
        {
            string type = GetCSharpType(GetDotNetType(column));
            if (!column.AllowDBNull)
            {
                return type;
            }
            else
            {
                if (type == "string")
                {
                    return type;
                }
                else
                {
                    return type + "?";
                }
            }
        }

        public string GetCSharpTypeDefaultValue(string type)
        {
            type = GetCSharpType(type);
            if (dictCSharpTypeDefValue.ContainsKey(type)) return dictCSharpTypeDefValue[type];
            return "null";
        }

        public string GetCSharpTypeDefaultValue(ColumnSchema column)
        {
            if (column.AllowDBNull)
            {
                return "null";
            }

            if (IsEnumType(column))
            {
                return "(" + GetEnumName(column) + ")0";
            }
            else
            {
                return GetCSharpTypeDefaultValue(GetDotNetType(column));
            }
        }

        public bool IsTimeStampColumn(ColumnSchema column)
        {
            return column.NativeType.IndexOf("timestamp") > -1;
        }

        public bool IsEnumType(ColumnSchema column)
        {
            return !String.IsNullOrEmpty(GetEnumName(column));
        }

        public string GetEnumName(ColumnSchema column)
        {
            if (column.NativeType == "enum")
            {
                return StringUtil.ToPascalCase(column.Name);
            }
            else
            {
                if (column.SystemType.Name == "Int32" ||
                    column.SystemType.Name == "Int64" ||
                    column.SystemType.Name == "Int16")
                {
                    string colName = GetColumnFullName(column);
                    if (colName.EndsWith("type") || 
                        colName.EndsWith("attribute") || 
                        colName.EndsWith("result") ||
                        colName.EndsWith("level") ||
                        colName.EndsWith("state") ||
                        colName.EndsWith("status") ||
                        colName.EndsWith("priority"))
                    {
                        if (colName.EndsWith("entity_type"))
                        {
                            return "EntityType";
                        }
                        else
                        {
                            string tableName = GetCleanTableName(column.Table);
                            if (tableName.EndsWith("_info")) tableName = tableName.Substring(0, tableName.Length - 5);
                            tableName += "_" + colName;
                            return StringUtil.ToPascalCase(tableName);
                        }

                        //if (colName == "type" || colName == "attribute")
                        //{
                        //    string tableName = GetCleanTableName(column.Table);
                        //    if (tableName.EndsWith("_info")) tableName = tableName.Substring(0, tableName.Length - 5);
                        //    tableName += "_" + colName;
                        //    return StringUtil.ToPascalCase(tableName);
                        //}
                        //else
                        //{
                        //    return StringUtil.ToPascalCase(column.Name);
                        //}
                    }
                }
            }
            return null;
        }

        public bool HasForeignObject(ColumnSchema column)
        {
            string colName = GetColumnFullName(column).ToLower();
            return colName.EndsWith("_id") && !colName.StartsWith("parent_");
        }

        public string GetForeignObjectName(ColumnSchema column, NameType nameType)
        {
            string colName = GetColumnFullName(column);
            if (HasForeignObject(column))
            {
                TableKeySchema fkey = null;
                foreach (TableKeySchema tk in column.Table.Keys)
                {
                    if (tk.Name.ToLower().EndsWith(column.Name.ToLower()))
                    {
                        fkey = tk;
                        break;
                    }
                }

                switch (nameType)
                {
                    case NameType.PropertyType:
                        if (fkey != null)
                        {
                            return GetName(fkey.PrimaryKeyTable, NameType.ClassName);
                        }
                        else
                        {
                            if (colName.IndexOf("er_") > -1 || colName.IndexOf("or_") > -1)
                            {
                                return GetName("user_info", NameType.ClassName);
                            }
                            else if (colName.IndexOf("entity") > -1)
                            {
                                return "IEntity";
                            }
                            else
                            {
                                return GetName(colName, NameType.ClassName);
                            }
                        }
                    case NameType.PropertyDescription:
                        return GetName(column, nameType).Replace("ID", "");//"ʵ�����ID");
                    default:
                        if (colName.ToLower().EndsWith("_id")) colName = colName.Substring(0, colName.Length - "_id".Length);
                        return GetName(colName, nameType);
                }
            }
            else
            {
                return GetName(column, nameType);
            }
        }
    }
}
